﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using System.Web.Mvc;
using System.Reflection;
using System.Data.Linq.Mapping;
using Suteki.Common.Extensions;

namespace BulletOnRails4NET.Models
{
    public abstract class CrudModel
    {
        private PropertyInfo _primary_key_prop;
        public virtual int GetId()
        {
            if (_primary_key_prop == null)
                _primary_key_prop = GetType().GetPrimaryKey();
            int id = (int)_primary_key_prop.GetValue(this, null);
            return id;
        }

        public virtual string GetAsLabel()
        {
            return ToString();
        }

        public virtual bool IsValid
        {
            get
            {

                return (GetRuleViolations().Count() == 0);
            }
        }
        public virtual List<RuleViolation> GetRuleViolations()
        {
            List<RuleViolation> violations = RuleViolation.CheckBasicDBCobnstraints(this);
            //violations.AddRange(GetCustomRulesViolations());
            return violations;
        }

        protected void ValidateBeforeSave()
        {
            if (!IsValid)
            {
                List<RuleViolation> violations = GetRuleViolations();
                StringBuilder error_message = new StringBuilder("Rule violations prevent saving");
                foreach (RuleViolation violation in violations)
                {
                    error_message.AppendLine(violation.ErrorMessage);
                }
                throw new RuleViolationException(error_message.ToString(),
                    violations);
            }
        }

        //Returns the string value of the properties passed. properties must be properites that belong
        // to the class. The values will be in the same order as the properties array.
        public String[] GetPropertyValuesAsString(PropertyInfo[] properties)
        {
            String[] values = new String[properties.Length];
            int idx = 0;
            foreach (PropertyInfo prop in properties)
            {
                if (AssociationProperties.ContainsKey(prop.Name))
                {
                    //Get the label representation of the associated object
                    PropertyInfo assoc_prop = AssociationProperties[prop.Name];
                    object prop_value = assoc_prop.GetValue(this, null);
                    if (prop_value == null)
                        values[idx] = "";
                    else
                    {
                        if (prop_value is CrudModel)
                            values[idx] = ((CrudModel)prop_value).GetAsLabel();
                        else
                            values[idx] = prop_value.ToString();
                    }

                }
                else
                {
                    object prop_value = prop.GetValue(this, null);
                    if (prop_value == null)
                        values[idx] = "";
                    else
                        values[idx] = prop_value.ToString();
                }
                idx++;
            }
            return values;
        }


        private static Dictionary<string, PropertyInfo> _association_properties;
        Dictionary<string, PropertyInfo> AssociationProperties
        {
            get
            {
                if (_association_properties == null)
                    _association_properties = GetFkAssociationsMapping();
                return _association_properties;
            }
        }

        //Returns a dictionary mapping the FK properties to their Association property
        protected Dictionary<string, PropertyInfo> GetFkAssociationsMapping()
        {
            PropertyInfo[] model_properties = this.GetType().GetProperties();
            object[] info;
            AssociationAttribute assoc_attr;
            Dictionary<string, PropertyInfo> mapping = new Dictionary<string, PropertyInfo>();
            foreach (PropertyInfo prop in model_properties)
            {
                //Get the Linq related info
                info = prop.GetCustomAttributes(typeof(AssociationAttribute), true);
                // Check if it is a Assoc property
                if (info.Length == 1)
                {
                    assoc_attr = (AssociationAttribute)info[0];
                    if (assoc_attr.IsForeignKey)
                    {
                        mapping.Add(assoc_attr.ThisKey, prop);
                    }
                }
            }
            return mapping;
        }
        //public CrudModel CreateInstance();

        //TDatabaseObjectClass Get(TRepository repository, int id);
    }
}
